/***************************************************************************
 *   Copyright (C) 2010 by Lasse Liehu                                     *
 *   lliehu@kolumbus.fi                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "win_functions.h"

#include <QString>

namespace notifier
{

/**
  * This function sets autostart (for current user only) for the executable
  * given in @c exePath.
  * @param appName The name of the registry value
  * @param exePath An absolute path to the executable
  * @returns True when succeeded, false otherwise
  */
bool setAutostartForApp(const QString& appName, const QString& exePath)
{
    // Open registry key
    HKEY key;
    DWORD err;
    if ((err = RegOpenKeyExW(HKEY_CURRENT_USER,
                     L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0,
                     KEY_SET_VALUE, &key)) != ERROR_SUCCESS)
    {
        logWin32ErrForDbg(err, "setAutostartForApp", "RegOpenKeyEx");
        return false;
    }

    // Prepare appName and exePath for use
    QString exePathQ = exePath; // q stands for "quoted"
    exePathQ.insert(0, '\"');
    exePathQ.append('\"');
    exePathQ.replace('/', '\\');
    wchar_t *appNameW = new wchar_t[appName.length()+1];
    wchar_t *exePathW = new wchar_t[exePathQ.length()+1];
    int length = appName.toWCharArray(appNameW);
    appNameW[length] = L'\0';
    length = exePathQ.toWCharArray(exePathW);
    exePathW[length] = L'\0';

    // Set the value
    if ((err = RegSetValueExW(key, appNameW, 0, REG_SZ,
                      reinterpret_cast<const BYTE*>(exePathW),
                      sizeof(wchar_t)*(length+1))) != ERROR_SUCCESS)
    {
        logWin32ErrForDbg(err, "setAutostartForApp", "RegSetValueEx");

        delete [] appNameW;
        delete [] exePathW;
        RegCloseKey(key);
        return false;
    }
    delete [] appNameW;
    delete [] exePathW;
    RegCloseKey(key);
    return true;
}

/**
  * This function removes autostart (from current user only) from the
  * executable with registry value named @c appName
  * @param appName Name of the registry value
  * @returns True when succeeded, false otherwise
  */
bool unsetAutostartForApp(const QString& appName)
{
    // Open registry key
    HKEY key;
    DWORD err;
    if ((err = RegOpenKeyExW(HKEY_CURRENT_USER,
                     L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0,
                     KEY_SET_VALUE, &key)) != ERROR_SUCCESS)
    {
        logWin32ErrForDbg(err, "unsetAutostartForApp", "RegOpenKeyEx");
        return false;
    }

    // Prepare appName for use
    wchar_t *appNameW = new wchar_t[appName.length()+1];
    int length = appName.toWCharArray(appNameW);
    appNameW[length] = L'\0';

    // Delete the value
    if ((err = RegDeleteValueW(key, appNameW)) != ERROR_SUCCESS)
    {
        logWin32ErrForDbg(err, "unsetAutostartForApp", "RegDeleteValue");
        delete [] appNameW;
        RegCloseKey(key);
        return false;
    }
    else
    {
        delete [] appNameW;
        RegCloseKey(key);
        return true;
    }
}

/**
  * This function checks if autostart for executable with registry value
  * named @c appName exists (for the current user)
  * @param appName The name of the registry value
  * @returns True if it exists, false if it doesn't or an error occurred
  */
bool hasAutostartForApp(const QString& appName)
{
    // Open registry key
    HKEY key;
    DWORD err;
    if ((err = RegOpenKeyExW(HKEY_CURRENT_USER,
                     L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0,
                     KEY_QUERY_VALUE, &key)) != ERROR_SUCCESS)
    {
        logWin32ErrForDbg(err, "hasAutostartForApp", "RegOpenKeyEx");
        return false;
    }

    // Prepare appName for use
    wchar_t *appNameW = new wchar_t[appName.length()+1];
    int length = appName.toWCharArray(appNameW);
    appNameW[length] = L'\0';

    // Check if the value exists
    err = RegQueryValueExW(key, appNameW, 0, NULL, NULL, NULL);
    delete [] appNameW;
    RegCloseKey(key);
    if (err == ERROR_SUCCESS) return true;
    else if (err == ERROR_FILE_NOT_FOUND) return false;
    else
    {
        logWin32ErrForDbg(err, "hasAutostartForApp", "RegQueryValueEx");
        return false;
    }
}

/**
  * This function logs the given Win32 error (got from GetLastError() ) to
  * qWarning().
  * @param err The errorcode from GetLastError
  * @param funcName The function that failed, can be null (means no name given)
  * @param context E.g. the name of the class or something; cannot be null
  */
void logWin32ErrForDbg(DWORD err, const char *context, const char *funcName)
{
    // TODO FIXME is something wrong as this function doesn't seem to be able to
    // output the error message all the time
    LPVOID lpMsgBuf = 0;
    FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                   FORMAT_MESSAGE_FROM_SYSTEM |
                   FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0,
                   reinterpret_cast<LPWSTR>(&lpMsgBuf), 0, NULL);
    QString errMsg = QString::fromWCharArray(reinterpret_cast<LPWSTR>(lpMsgBuf));
    QByteArray errMsgLat1 = errMsg.toLatin1();
    if (funcName)
    {
        qWarning("error: %s: %s: errCode %i: %s", context, funcName, err,
                 errMsgLat1.constData() );
    }
    else
    {
        qWarning("error: %s: [unknown function]: errCode %i: %s",
                 context, err, errMsgLat1.constData() );
    }
    LocalFree(lpMsgBuf);
}

} // namespace notifier
